#include "StdAfx.h"
#include "PickPathDlg.h"
#include <algorithm>
#include <functional>

CPickFileItem::CPickFileItem(const TCHAR* name, const TCHAR* fullName, DWORD attribs)
  : CSelectorListItem(name), m_fullName(fullName), m_attribs(attribs)
{
  SHFILEINFO sfi = {0};
  if (::SHGetFileInfo(m_fullName, 0, &sfi, sizeof(sfi), SHGFI_ICON))
  {
    m_icon = sfi.hIcon;
  }
}

void CPickFileItem::Draw(HDC hdc, const RECT& rc) const
{
  DrawBackground(hdc, rc);

  CRect rctxt = rc;
  rctxt.left += CTheme::iListItemIndent;
  rctxt.right = rctxt.left + rctxt.Height() - CTheme::iListItemIndent * 2;
  rctxt.top += CTheme::iListItemIndent;
  rctxt.bottom -= CTheme::iListItemIndent;
  if (m_icon.m_hIcon)
    ::DrawIconEx(hdc, rctxt.left, rctxt.top, m_icon.m_hIcon, rctxt.Width(), rctxt.Height(), 0, NULL, DI_NORMAL);

  rctxt.left = rctxt.right + CTheme::iListItemIndent;
  rctxt.right = rc.right - CTheme::iListItemIndent;
  rctxt.top = rc.top;
  rctxt.bottom = rc.bottom;
  this->DrawText(hdc, rctxt, m_text);
}

void CPickFileUpItem::Draw(HDC hdc, const RECT& rc) const
{
  DrawBackground(hdc, rc);

  CRect rctxt = rc;
  rctxt.left += CTheme::iListItemIndent;
  rctxt.right = rctxt.left + rctxt.Height() - CTheme::iListItemIndent * 2;
  rctxt.top += CTheme::iListItemIndent;
  rctxt.bottom -= CTheme::iListItemIndent;
  m_image.Draw(hdc, rctxt);

  rctxt.left = rctxt.right + CTheme::iListItemIndent;
  rctxt.right = rc.right - CTheme::iListItemIndent;
  rctxt.top = rc.top;
  rctxt.bottom = rc.bottom;
  this->DrawText(hdc, rctxt, m_text);
}

LRESULT CPickFileDlg::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
  m_folder = m_pickData.initialFolder;
  if (m_folder.IsEmpty() || m_folder[0] != TEXT('\\'))
    m_folder.Insert(0, TEXT('\\'));

  m_btnUp.SetImages(CTheme::GetImage(timButtonBg), CTheme::GetImage(timFolderUp), CTheme::GetImage(timFolderUp));
  m_btnUp.Create(m_hWnd, NULL, NULL, WS_CHILD | WS_VISIBLE, 0, IDC_PICKFILE_UP);
  m_btnRoot.SetImages(CTheme::GetImage(timButtonBg), CTheme::GetImage(timFolderRoot), CTheme::GetImage(timFolderRoot));
  m_btnRoot.Create(m_hWnd, NULL, NULL, WS_CHILD | WS_VISIBLE, 0, IDC_PICKFILE_ROOT);

  FillList();

  return bHandled = FALSE;
}

LRESULT CPickFileDlg::OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
  if (wParam != SIZE_MINIMIZED)
  {
    UpdateSize();
    return bHandled = TRUE;
  }
  return bHandled = FALSE;
}

void CPickFileDlg::UpdateSize()
{
  bool isRoot = (m_folder.Compare(TEXT("\\")) == 0);
  m_btnUp.ShowWindow(isRoot ? SW_HIDE : SW_SHOW);
  m_btnRoot.ShowWindow(isRoot ? SW_HIDE : SW_SHOW);

  CRect rcClient;
  GetClientRect(&rcClient);
  int y = 0;
  if (!isRoot)
  {
    m_btnUp.MoveWindow(0, 0, CTheme::iSelectorListItemSize, CTheme::iSelectorListItemSize);
    m_btnRoot.MoveWindow(CTheme::iSelectorListItemSize, 0, CTheme::iSelectorListItemSize, CTheme::iSelectorListItemSize);
    y = CTheme::iSelectorListItemSize;
  }
  m_pList->MoveWindow(0, y, rcClient.Width(), rcClient.Height() - y);
}

void CPickFileDlg::OnFileSelect(UINT uNotifyCode, int nID, CWindow wndCtl)
{
  CPickFileItem* pItem = (CPickFileItem*)m_pList->GetSelectedItem();
  if (!pItem)
    return;
  // check if folder
  if ((pItem->GetAttribs() & FILE_ATTRIBUTE_DIRECTORY) &&
      (m_pickData.attrExclude & FILE_ATTRIBUTE_DIRECTORY))
  {
    OnFolderOpen(uNotifyCode, nID, wndCtl);
  }
  // else
  else if ((pItem->GetAttribs() & m_pickData.attrRequired) &&
      !(pItem->GetAttribs() & m_pickData.attrExclude))
  {
    m_selection = pItem->GetFullName();
    EndDialog(IDOK);
  }
}

void CPickFileDlg::OnFolderOpen(UINT uNotifyCode, int nID, CWindow wndCtl)
{
  CPickFileItem* pItem = (CPickFileItem*)m_pList->GetSelectedItem();
  if (pItem)
  {
    m_folder += pItem->GetText();
    FillList();
    UpdateSize();
    m_pList->Redraw();
  }
}

void CPickFileDlg::OnFolderUp(UINT uNotifyCode, int nID, CWindow wndCtl)
{
  // last char is "\\"
  for (int i = m_folder.GetLength() - 2; i >= 0; i--)
  {
    if (m_folder[i] == TEXT('\\'))
    {
      m_folder.Truncate(i);
      FillList();
      UpdateSize();
      m_pList->Redraw();
      break;
    }
  }
}

void CPickFileDlg::OnFolderRoot(UINT uNotifyCode, int nID, CWindow wndCtl)
{
  m_folder = TEXT("\\");
  FillList();
  UpdateSize();
  m_pList->Redraw();
}

void CPickFileDlg::SetSelection(const CString& file)
{
  CString sFile = file;
  int fname = sFile.ReverseFind(TEXT('\\'));
  if (fname >= 0)
  {
    sFile = sFile.Mid(fname + 1);
  }
  CListSelectorDlg::SetSelection(sFile);
}

void CPickFileDlg::FillList()
{
  if (m_folder.IsEmpty() || m_folder[m_folder.GetLength() - 1] != TEXT('\\'))
    m_folder += TEXT("\\");

  m_pList->Clear();

  WIN32_FIND_DATA fd = {0};
  CString sFile = m_folder;
  sFile += TEXT("*.*");
  HANDLE hFind = ::FindFirstFile(sFile, &fd);
  if (hFind == INVALID_HANDLE_VALUE)
  {
    Logger::LogFmt(lsWarning, TEXT("No files found for PickFile list under '%s'"), sFile.GetString());
    if (m_folder.Compare(TEXT("\\")) != 0)
    {
      m_folder = TEXT("\\");
      FillList();
    }
    return;
  }

  CFindFileMap mapDirs;
  CFindFileMap mapFiles;

  do
  {
    if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    {
      mapDirs.insert(std::make_pair(fd.cFileName, fd.dwFileAttributes));
    }
    else
    {
      if (fd.dwFileAttributes & m_pickData.attrExclude)
        continue;
      if (!(fd.dwFileAttributes & m_pickData.attrRequired))
        continue;

      if (m_pickData.allowedExts.size() > 0)
      {
        TCHAR* ext = ::_tcsrchr(fd.cFileName, TEXT('.'));
        if (ext)
        {
          ext++;
          bool allowed = false;
          for (CStringVector::iterator iter = m_pickData.allowedExts.begin(); iter != m_pickData.allowedExts.end(); iter++)
          {
            if (iter->Compare(TEXT("*")) == 0 || iter->CompareNoCase(ext) == 0)
            {
              allowed = true;
              break;
            }
          }
          if (!allowed)
            continue;
        }
      }
      mapFiles.insert(std::make_pair(fd.cFileName, fd.dwFileAttributes));
    }
  }
  while(::FindNextFile(hFind, &fd));

  ::FindClose(hFind);

  CString shortName, fullName;
  CFindFileMap::iterator iter;
  bool canSelect = !(m_pickData.attrExclude & FILE_ATTRIBUTE_DIRECTORY);
  for (iter = mapDirs.begin(); iter != mapDirs.end(); iter++)
  {
    fullName = m_folder;
    fullName += iter->first;
    CPickFileFolderItem* pItem = new CPickFileFolderItem(iter->first, fullName, iter->second, canSelect);
    m_pList->Add(pItem);
  }
  for (iter = mapFiles.begin(); iter != mapFiles.end(); iter++)
  {
    fullName = m_folder;
    fullName += iter->first;
    shortName = iter->first;
    if (!m_pickData.showExts)
    {
      int ext = shortName.ReverseFind(TEXT('.'));
      if (ext >= 0)
        shortName.Truncate(ext);
    }
    CPickFileItem* pItem = new CPickFileItem(shortName, fullName, iter->second);
    m_pList->Add(pItem);
  }
}